Linux expect自动交互脚本入门

Expect is a program that “talks” to other interactive programs according to a script. Following the script, Expect knows what can be expected from a program and what the correct response should be. An interpreted language provides branching and high-level control structures to direct the dialogue. In addition, the user can take control and interact directly when desired, afterward returning control to the script.

expect基础语法

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 命令行参数 
# $argv,参数数组,使用[lindex $argv n]获取,$argv 0初始化为脚本名字
# $argc,参数个数
set username [lindex $argv 0] # 获取第0个参数
set passwd [lindex $argv 1] # 获取第1个参数
set TOKEN $expect_out(buffer): 获取上一个键盘输入保存到TOKEN变量中

set timeout 30 # 设置超时30s

# spawn是expect内部命令,开启ssh连接
spawn ssh -l username 192.168.1.1

# 判断上次输出结果里是否包含以“password:”开头的字符串,如果有则立即返回,否则就等待一段时间(timeout)后返回
expect "password:*"

# 发送内容ispass(密码),与手工输入密码ispass的动作等效
send "ispass\r"

# 发送内容给用户
send_user "$argv0 [lrange $argv 0 2]\n"
send_user "It's OK\r"
# 执行完成后保持交互状态,控制权交给控制台(可以继续手工输入等操作)。否则会完成后会退出。
interact
spwan命令

spawn命令用于启动一个进程, 后边接上你要执行的命令,例如spawn ssh relay01.baidu.com

interact命令

interact命令用于脚本执行完简单的命令后人手动介入,只在所属的spawn进程空间有效

expect的三种使用格式
  • 并行:只要其中的任何一个pattern能够匹配,那么这个expect就算是完成了一次匹配

    expect {
        pattern1 {
            command2 }
        pattern2 {
            command2 }
    }
    
    # 示例
    expect {
       "hi"    { send "You said hi\r" }
       "hello" { send "Hello yourself\r" }
       "bye"   { send "That was unexpected\r" }
    }
    
  • 串行:所有的匹配被依次满足了之后才算整个语句完成

    expect pattern command
    expect pattern2 command2
    
    # 串行示例
    expect "username:"
    send "$userid\r"
    expect "password:" { send "$mypassword\r" }
    
    # 利用exp_continue并行实现串行,continue执行下一个pattern
    expect {   
        "*yes/no" { send "yes\r"; exp_continue }
         "*password:" { send "$mypassword\r" }   
    }
    
shell脚本接收多个参数
  1. expect脚本可以接受从bash传递过来的参数.可以使用[lindex $argv n]获得,n从0开始,分别表示第一个,第二个,第三个….参数
  2. $argc为命令行参数的个数,$argv0为脚本名字本身,$argv为命令行参数。[lrange $argv 0 0]表示第1个参数,[lrange $argv 0 4]为第一个到第五个参数。
  3. 执行./test.sh 1 2 3,脚本内容如下:
1
2
3
4
5
6
7
8
#!/usr/bin/expect -d
set username [lindex $argv 0]
set password [lindex $argv 1]
set server [lindex $argv 2]
send_user "UserName is $username\n"
send_user "PassWord is $password\n"
send_user "Server is $server\n"
send_user "Total arg num is $argc\n"

bash脚本直接调用expect进行交互

  • Here document实现, expect脚本可以使用bash中定义的变量
1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
host="USER@HOST"
password="PASSWORD"
cmd="command_list"

expect <<EOF
spawn ssh $host "$cmd"
expect {
"*(yes/no)?" { send "yes\r";exp_continue }
"*assword:" { send "$password\r" }
}
expect eof
EOF
  • 直接使用set来定义变量并在expect脚本中使用
1
2
3
4
5
6
7
8
9
10
#!/bin/bash
expect -c '
set $PASSWORD="123qwe"
spawn ssh USER@HOST "commands"
expect {
"*(yes/no)?" { send "yes\r";exp_continue }
"*assword:" { send "$PASSWORD\r" }
}
expect eof
'

如果希望保持登录,去掉上面代码的ssh后的命令列表并且将expect eof改成interact即可

更多示例

  1. expect交互脚本示例
  2. Linux Expect 简介和使用实例

开发机登陆脚本示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#!/usr/bin/expect

# 登录relay
spawn ssh relay01.baidu.com

# 定义变量
set PIN "123321"
set HOST "hz01-bos-dev-r15-01-005.hz01"
set PASSWORD ""

# 执行脚本时带上参数,以空格分开
# set TOKEN [lindex $argv 0]

# 打开调试模式
#exp_internal 1
# relay PIN码 + Token(Token需手动输入)
expect {
-re "password:*" {
send_user "PIN:****** + Token:"
expect_user {
-timeout -1
-re "(.*)\n"
}
set TOKEN $expect_out(buffer)
send "$PIN$TOKEN\r"
exp_continue
}
# 自动登录开发机
-re "-bash-baidu-ssl*" {
if { "$HOST" != "" } {
send "ssh --silent $HOST\r"
# [可选]自动开发机输入密码
if { "$PASSWORD" != "" } {
expect -re "password:" { send "$PASSWORD\r" }
}
}
}
}
# 关闭调试模式
#exp_internal 0
interact
exit